/**
 * Class PairwiseAlignmentData : holds the the data for two aligned sequences; 
 * Called from annotatePave: CoreAnnotator.doHomology: multiple times with the same dpAlgo
 * Called from viewPave: ClusterView.pairwiseAlignOnCluster for a given instance
 * 
 * 1. Constructor and methods for the class
 * 2. Static methods for doing the alignments
 * 		alignDBhits is called for protein to sequence
 * 		alignPairwise is called for sequence to sequence
 * 		they both return a PairwiseAlignment object
 * 3. Static methods for building lists of alignments for display
 * 		multiDBhitAlign align multiple proteins to one or more unitrans
 * 		multiPairAlign  align two unitrans
 * 		they both return a list of PairAlignment objects
 */
package jpave.dataholders;

import java.lang.Math;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Vector;

import util.algorithms.PairwiseAligner;
import util.Debug.ErrorReport;

public class AlignmentData 
{
	static int debug = 0;
	static boolean showHSPs=true; // CAS uses for testing
	public static final double AAsimPercentCutoff = 80.0;
	public static final int AAlenCutoff = 100;
	public static final double NTsimPercentCutoff = 80.0;
	public static final int NTlenCutoff = 200;
	
	public static final short firstResult=0; 
	public static final short bestResult=1; 
	public static final short allResult=2;
	public static final short frameResult=3; // frame of protein alignment

	private AlignmentData ( String label, 
				PairwiseAligner dpAlgo,
				ContigData ctg1, ContigData ctg2,
				SequenceData seq1, SequenceData seq2,
				int nFrame1, int nFrame2, BlastHitData hit,
				boolean isP)
	{
	    seqData1 = seq1;					
	    seqData2 = seq2;
		nLength1 = seqData1.getLength(); 	
		nLength2 = seqData2.getLength();
		strName1 = seqData1.getName();
		strName2 = seqData2.getName();	
		isNtAlign =   seqData1.isDNA();
		hitData = hit;
		strID = label;
		bIsProteinDB = isP; 
		
		setLowHighIndex();
		setDPinfo(dpAlgo);
		setCtgInfo(ctg1, ctg2);

	    	if ( !isNtAlign) {
		    	nAAFrame1 = nFrame1;	
		    	nAAFrame2 = nFrame2;	
	    	} 
	 }

	 private void setDPinfo (PairwiseAligner dpAlgo)
	 {
 		nHSRmatch = 	dpAlgo.getHSRmatch(); 
 		nHSRlen = 	dpAlgo.getHSRlen();
		nHSRstart = 	dpAlgo.getHSRstart(); 
		nHSRend = 	dpAlgo.getHSRend();
		nHSRstops = dpAlgo.getHSRstops();
		if (nHSRlen != 0) pHSRsim = ((double) nHSRmatch/ (double) nHSRlen) * 100.0;
		nOLPlen = 	dpAlgo.getOLPlen(); 
		nOLPmatch = 	dpAlgo.getOLPmatch();
	 }
	 
	 private void setCtgInfo (ContigData ctg1, ContigData ctg2)
	 {
	    ctgData1 = ctg1; 	
	    strName1 = ctgData1.getContigID();
	    if (!bIsProteinDB) { 
	    		longestCdsData1 = ContigData.convertToAligned ( seqData1, ctgData1.getORFCoding() );
	    		proteinCdsData1 = ContigData.convertToAligned ( seqData1, ctgData1.getProteinCoding() );
		    nORF1 = ctg1.getBestCodingFrame ();
	    }
		
		if (ctg2 != null) { // pairwise
			ctgData2 = ctg2;	
			strName2 = ctgData2.getContigID();
			if (!bIsProteinDB) { // CAS Aug 2012
				longestCdsData2 = ContigData.convertToAligned ( seqData2, ctgData2.getORFCoding() );	
				proteinCdsData2 = ContigData.convertToAligned ( seqData2, ctgData2.getProteinCoding() );
				nORF2 = ctg2.getBestCodingFrame ();
			}
		}
		else {              // DB hit        
		    ctgData2 = null;
		    strName2 = seqData2.getName();
		}
	 }
	 
	private void setLowHighIndex() {
		// note different in max and mix for graphics vs sequence
		int low1= seqData1.getLowIndex(), high1= seqData1.getHighIndex();
		int low2= seqData2.getLowIndex(), high2= seqData2.getHighIndex();
		
		seqLowIndex =    Math.max (low1, low2 ); 
		graphLowIndex =  Math.min (low1, low2 ); 
		
		seqHighIndex =   Math.min (high1, high2 ); 
		graphHighIndex = Math.max (high1, high2 ); 

		// just a small overhang so longest is obvious for sequence view
		int lowest = Math.min (low1, low2 );
		for (int i = 1; i <= 15 && seqLowIndex > lowest; i++) seqLowIndex--;
	
		int highest = Math.max (high1, high2 ); 
		for (int i = 1; i <= 15 && seqHighIndex < highest; i++) seqHighIndex++;
	}
	 /******************** Methods for drawing *************************
	 * the following is called by PairwiseAlignmentPanel.setBasesPerPixel for display 
	 */
	 public Vector<String> getDescription (int num )
	 { 
		Vector<String> lines = new Vector<String> ();    

		String s = "";
		if (num != 0) s = num + ". ";
		if (isNtAlign) s+= "NT "; else s += "AA ";
		s += getOLPsim() + "% ";
				
	    	if (ctgData2 == null) {
	    		s += " ORF: " + ctgData1.getORFframe() + "  ";
	    		s += " " + hitData.getHitBlast();
	    		String dec = seqData2.getDBdesc();
	    		String tx= hitData.getDBtypeTaxo();
	    		s += "  " + tx + ": " + dec;
	    	}
	    	else s += "  " + hitData.getHitBlast(isNtAlign);
	
	    	lines.add(s);
	    	return lines;
	}
	 
	// This ORF stuff is not being used right now because orientation problems
    /****  Sequence 1 attributes  *******/
	public CodingRegion getORFCoding1 ( ) { return longestCdsData1; }
	public CodingRegion getProteinCoding1 ( ) { return proteinCdsData1; }
	public SequenceData getSequence1 ( ) { return seqData1; } // seq1 is sequence to be drawn
	public String getName1 ( ) { return strName1; }	
	public String getDisplayStr1 ( ) {
		return getFrameForDisplay(strName1, nAAFrame1, nORF1, isNtAlign);
	}	
	/****  Sequence 2 attributes ****/
	public CodingRegion getORFCoding2 ( ) { return longestCdsData2; }
	public CodingRegion getProteinCoding2 ( ) { return proteinCdsData2; }
	public SequenceData getSequence2 ( ) { return seqData2; }; // seq2 to be drawn
	public String getName2 ( ) { return strName2; }	 	
	public String getDisplayStr2 ( ) { 
		return getFrameForDisplay(strName2, nAAFrame2, nORF2, isNtAlign);
	};

	private String getFrameForDisplay(String name, int frame, int orf, boolean isNtAlign)
	{
		if ( name == null) return "";
		String str = name;
		if (frame == 0 && orf == 0) { // Protein sequence
			str += "         ";
		}
		else if ( !isNtAlign ) { // Translated sequence
			if (frame < 0) str+= " (RF " + frame + ")";
			else str+= " (RF  " + frame + ")"; // extra blank
		}
		return str;
	}
	public String getID ( ) { return strID; }
	public void setID ( String str ) { strID = str; }
	
	// Called from PairwiseAlignmentPanel to determine the ruler

	public int getLowIndex (boolean drawSeq ) { 
		if (drawSeq) return seqLowIndex;
		return graphLowIndex;
	}
	public int getHighIndex ( boolean drawSeq) { 
		if (drawSeq) return seqHighIndex;
		return graphHighIndex;
	}    	
	/********** Computed values  **********/
	public double getHSRratio() {
		if (nHSRlen == 0) return 0;
		int nLength = Math.min(nLength1, nLength2);		
		return (double) nHSRlen/nLength; 
	}
	public double getOLPratio()  
	{ 
		if (nOLPlen == 0) return 0;
		int nLength = Math.min(nLength1, nLength2);		
		return (double) nOLPlen/nLength; 
	} 		

	public double getHSRsim ( ) { return pHSRsim;}	
	public int getOLPsim ( ) 
	{   
	    if ( nOLPlen == 0 ) return 0;
	    double p = ((double)nOLPmatch/(double)nOLPlen) * 100.0;
	    return (int) p;
	}	
	public int getHSRlen ( ) { return nHSRlen; };
	public int getHSRmatch ( ) { return nHSRmatch; }	
	public int getHSRstart ( ) { return nHSRstart; }
	public int getHSRend ( ) { return nHSRend; }	
	public int getOLPlen ( ) { return nOLPlen;}
	public int getOLPmatch ( ) { return nOLPmatch;}
	public int getLength1 ( ) { return nLength1;}
	public int getLength2 ( ) { return nLength2;}
	
	public boolean hasAAalignment() { return (bestAAalignData != null);} 
	public AlignmentData getAApwData ( ) {return bestAAalignData;}
	public BlastHitData getHitData() { return hitData;}
	
	public int getFrame1 () { 
		if (bestAAalignData!= null) return bestAAalignData.nAAFrame1;
		else return nAAFrame1;
	}
	public int getFrame2 () { 
		if (bestAAalignData!= null) return bestAAalignData.nAAFrame2;
		else return nAAFrame2;
	}
	
	public boolean isProteinAlignment() { return isNtAlign;}

	public void releaseSequences ( ) 
	{
	    	seqData1 = null;
	    	seqData2 = null;	
	    	ctgData1 = null;
	    	longestCdsData1 = null;
	    	proteinCdsData1 = null;
	    	ctgData2 = null;
	    	longestCdsData2 = null;
	    	proteinCdsData2 = null;
	    	if ( bestAAalignData != null )
	        	bestAAalignData.releaseSequences();
	}

	/**************** XXX Instance variables ***********************/
	private boolean bIsProteinDB = false;
    private String strID = null;  // Only used in description
	private boolean isNtAlign = true;
	private int graphLowIndex = 0;
	private int graphHighIndex = 0;
	private int seqLowIndex = 0;
	private int seqHighIndex = 0;
    	
	private SequenceData seqData1 = null;
	private String strName1 = "";
	private int nLength1 = 0; 
	
	private SequenceData seqData2 = null;
	private String strName2 = "";
	private int nLength2 = 0; 
	
	private ContigData ctgData1 = null;
	private CodingRegion longestCdsData1 = null;
	private CodingRegion proteinCdsData1 = null;
	private int nORF1 = 0;

	private ContigData ctgData2 = null;
	private CodingRegion longestCdsData2 = null;
	private CodingRegion proteinCdsData2 = null;
	private int nORF2 = 0;
	
	private AlignmentData bestAAalignData = null;
	private int nAAFrame1 = 0;
	private int nAAFrame2 = 0;

	private BlastHitData hitData = null;
    private int nHSRmatch = 0;
    private int nHSRlen = 0;
    private double pHSRsim = 0.0;
    private int nHSRstart = 0;
    private int nHSRend = 0;
    private int nHSRstops = 0;
    private int nOLPmatch = 0;
    private int nOLPlen = 0;  
	
	/**************** STATIC ALIGNMENT METHODS *******************************/
	 /***********************************************
	 *				Unitrans to DB hits 
     * XXX MainTopRowTab.run 
     * the input is MainData that has one or two contigs
     * return a list of PairwiseAlignmentData objects for display
     * (1) Contig overview: all DB hits to unitrans
     * (2) Pairwise or select multiple contigs: multiple sets DB hits
     * 		to unitrans -- group and align by DB hit
     ***********************************************/
  
	
	// used by all alignment routines for current alignment
	final static boolean isNT=true;
	static int typeResult=frameResult; 
	static PairwiseAligner dpAlgoObj;
	static ContigData ctgDataI=null, ctgDataII=null;
	static String label;
	static private boolean isProteinDB = false;
	
	// results
	static Vector<AlignmentData> alignList = null;	
	public static int cntNewBest=0; // for core pairwise
	
    static private void clear(boolean flag) {
    		if (flag) dpAlgoObj.clear();
    		ctgDataI = ctgDataII = null;
    		label = "";
    }
	
    /*******************************************************
     * align selected or align selected all (i.e. all frames)
     * type is best or all_frames
     */
	static public Vector<AlignmentData> DBhitsAlignDisplay (
			String [] selected, MainData listData, short type, boolean isP) throws Exception 
	{
       dpAlgoObj = new PairwiseAligner ();
       alignList = new Vector<AlignmentData> ();
       typeResult = type;
       isProteinDB = isP;
        
        // just one contig in set
  	   TreeSet<String> contigSet = listData.getContigIDSet(); 
  	   Iterator<String> it = contigSet.iterator();
       ContigData ctgData1 = listData.findContig( (String)it.next() );	
		
		// Load all the DB hit data for the contig from the database	
        // the data comes from the db_unitrans_hits table and db_unique_hits
       ArrayList <SequenceData> dbSeqHitList = ctgData1.seqDataHitList();			
       if (dbSeqHitList==null) return alignList;
		        
       label = "";			
       for (int j = 0; j<dbSeqHitList.size(); j++)
       {
	        SequenceData dbSeqData = dbSeqHitList.get(j);
	        String hitID = dbSeqData.getName();
	        for (int i=0; i<selected.length; i++) {
	        		if (selected[i].equals(hitID)) {
	        			DBHitAlign ( ctgData1, dbSeqData);
	        			break;
	        		}
            }
       }
       clear(true);
       return alignList;
	}
	/************************************************
	 * 	align based on filter set
	 *  type is always frame
	 */
	static public 	Vector<AlignmentData> DBhitsAlignDisplay (
					int filtertype, MainData listData, boolean isP) throws Exception
	{
        dpAlgoObj = new PairwiseAligner ();
        alignList = new Vector<AlignmentData> ();
        isProteinDB = isP;
        typeResult = frameResult; // CAS 3/30/15 -- really didn't change anything
        
        // List of contigs to process
  	    TreeSet<String> contigSet = listData.getContigIDSet(); 
		Iterator<String> it = contigSet.iterator();
		
        ContigData ctgData1 = listData.findContig( (String)it.next() );	
		
		// Load all the DB hit data for the contig from the database	
        // the data comes from the db_unitrans_hits table and db_unique_hits
		ArrayList <SequenceData> dbSeqHitList = ctgData1.seqDataHitList();			
		if (dbSeqHitList==null) return alignList;
		        
		if (contigSet.size()==1) {
			label = "";		
			for (int j = 0; j<dbSeqHitList.size(); j++)
			{
                	SequenceData dbSeqData = dbSeqHitList.get(j);
                	if (filtertype != 0 && 
                		(dbSeqData.getDBfiltered() & filtertype)==0) continue;
                	
                	// database has top 3 per annoDB, we just want the top         
                	if (filtertype == 2 && dbSeqData.getBlastHitData().getBlastRank() != 1) continue;
                	
                	DBHitAlign ( ctgData1, dbSeqData);
            }
			clear(true);
			return alignList;
		}

		/******************************************************
		 * Shared DB hits for unitran pairs
		 */
 
        ContigData ctgData2 = listData.findContig( (String)it.next() );	
	    ArrayList <SequenceData> dbSeqList2 = ctgData2.seqDataHitList(); 
		if (dbSeqList2==null) return alignList;
		
		// make list of shared DB hits
		int nHits = 3;
		if (filtertype!=-1) nHits = dbSeqHitList.size();
		ArrayList<String> dbHitSet = new ArrayList<String>();
		
		for (int j = 0; j<dbSeqHitList.size() && dbHitSet.size() < nHits; j++)
		{
            SequenceData dbSeqData1 = dbSeqHitList.get(j);
            String hitname1 = dbSeqData1.getName();
            if (filtertype != 0 && 
            		(dbSeqData1.getDBfiltered() & filtertype)==0) continue;
            	
			for (int k = 0; k<dbSeqList2.size(); k++)
			{
	           	SequenceData dbSeqData2 = dbSeqList2.get(k);
	          	if (filtertype != 0 && 
	          		(dbSeqData2.getDBfiltered() & filtertype)==0) continue;
            	
	            	String hitname2 = dbSeqData2.getName();
	            	if (hitname1.equals(hitname2)) {
	            		dbHitSet.add(hitname2);
	            		break;
	            	}
			}
		}
		
		// make alignment list with DB hits grouped
		Iterator<String> db = dbHitSet.iterator();        
	    while (  db.hasNext() )
	    {
	        String sharedHitName = (String)db.next();
	
			for (int j = 0; j<dbSeqHitList.size(); j++)
			{
	            	SequenceData dbSeqData1 = dbSeqHitList.get(j);
	            	String hitname1 = dbSeqData1.getName();
	            	if (sharedHitName.equals(hitname1)) {
	                DBHitAlign (ctgData1, dbSeqData1);
	                	break;
	            	}
			}
			for (int j = 0; j<dbSeqList2.size(); j++)
			{
	            	SequenceData dbSeqData2 = dbSeqList2.get(j);
	            	String hitname2 = dbSeqData2.getName();
	            	if (sharedHitName.equals(hitname2)) {
	                DBHitAlign (ctgData2, dbSeqData2);
	                	break;
	            	}
			}
		}	
	    clear(true);
		return alignList;
	}
	
	static private void DBHitAlign ( 
			ContigData ctgData, SequenceData seqData) 
	{		
		ctgDataI = ctgData;
		ctgDataII = null;
		label = "";
		
		SequenceData seq1 = ctgData.getSeqData(); // for proteins
		if (!isProteinDB) seq1 = seq1.newSeqDataNoGap();
		
		SequenceData seq2 = seqData.newSeqDataNoQual();
		BlastHitData hitData = seqData.getBlastHitData();

		int f1 = hitData.getFrame1(seq1.getLength()); 
		
		if (!hitData.isProtein()) {		// annoDB hit is nucleotide
			seq2.setIsDNA(); // CAS 4/5/15 don't know what this was not set when loaded
			alignNT(seq1, seq2, f1, 0, hitData);
		}
		else if (isProteinDB)  // database is Protein
			alignAA(seq1, seq2, 0, 0, hitData);
		else 
			alignAA(seq1, seq2, f1, 0, hitData);
    }
    /***********************************************************************
     * 					Pairwise unitrans
     *************************************************************************
     * CoreAnnotator.doHomologyTests: pairwise Nucleotide against nucleotide alignment
     *************************************************************************/
	static public AlignmentData pairAlignCore ( PairwiseAligner dpAlgo, 
			ContigData ctgData1,  ContigData ctgData2, BlastHitData hitData,
			short type)
	{	
		dpAlgoObj = dpAlgo;
        alignList = new Vector<AlignmentData> ();

		ctgDataI = ctgData1;
		ctgDataII = ctgData2;
		typeResult = type;
		if (!hitData.getIsTself()) typeResult = bestResult;
		
		SequenceData ntSeq1 = ctgData1.getSeqData().newSeqDataNoGap();
		SequenceData ntSeq2 = ctgData2.getSeqData().newSeqDataNoGap();
			
		int	f1 = hitData.getFrame1(ntSeq1.getLength());
		int	f2 = hitData.getFrame2(ntSeq2.getLength());
		
		alignAA(ntSeq1, ntSeq2, f1, f2, hitData);			
		AlignmentData aa = alignList.get(0);
		if (aa==null) return null;
		
		alignNT(ntSeq1, ntSeq2, aa.nAAFrame1, aa.nAAFrame2, hitData);
		AlignmentData nt = alignList.get(1);
		if (nt==null) return null;
		
		nt.bestAAalignData = aa;
		
		clear(false);
		return nt;
	}	
    /*************************************************************************
     * XXX MainTopRowTab: display pairs of selected contigs or pairwise contigs
     * 7/29/11 we have disabled selected shared hits
     * so this is only called for a pair, that is, no needs for the loops
     *************************************************************************/
	   static public Vector<AlignmentData> pairAlignDisplay ( 
	    		MainData ctgListData, String tag, SessionData session, 
	    		short type ) throws Exception
	   {
		  try {
	        dpAlgoObj = new PairwiseAligner ();
	        alignList = new Vector<AlignmentData> ();
	        typeResult = type;
	        
    	    		TreeSet<String> contigSet = ctgListData.getContigIDSet(); 
	        TreeSet<String> innerLoopSet = new TreeSet<String> ();
	        innerLoopSet.addAll(contigSet); 
	        
	        Iterator<String> it1 = contigSet.iterator();        
	        while (  it1.hasNext() )
	        {
	            String ctgID1 = (String)it1.next();
	            ctgDataI = ctgListData.findContig( ctgID1 );
    				SequenceData ntSeq1 = ctgDataI.getSeqData().newSeqDataNoGap();
	            
	            innerLoopSet.remove( ctgID1 );
	            Iterator<String> it2 = innerLoopSet.iterator();
	            
	            while ( it2.hasNext() )
	            {
	                String ctgID2 = (String)it2.next();
	                ctgDataII = ctgListData.findContig( ctgID2 );
	    				SequenceData ntSeq2 = ctgDataII.getSeqData().newSeqDataNoGap();
	              
	                // hitData from pja_pairwise table
	    				// called/entered from MainTopRowTab for the two contigs
	                BlastHitData hitData = ctgListData.getSharedHits();
	                int f1=0, f2=0;
	                if (hitData!=null) {
	    					f1 = hitData.getFrame1(0); // from database
	    					f2 = hitData.getFrame2(0); 
	                }
    					if (f1==0 && f2 == 0) typeResult = bestResult;
    				
    					hitData.getFrame1(ntSeq1.getLength());
    					if (tag.contains("pair")) label =   
    							" (" + hitData.getCntSharedHits() + ")";
        			
    					alignNT(ntSeq1, ntSeq2, f1, f2, hitData);
    					alignAA(ntSeq1, ntSeq2, f1, f2, hitData);
	            }
	        }   
	        clear(true);
	        return alignList;
		  }
		  catch (Exception e) {
			   crash(e, "multiple pairwise align");
		  }
		  return null;
	 }

	/***********************************************************
	 *  XXX NT alignment
	 *********************************************************/
	static private void alignNT ( 
			SequenceData seq1, SequenceData seq2, int f1, int f2, 
			BlastHitData hitData) {
		 
		 AlignmentData bestData = null, curData=null;
		
		if (f2 == 0) f2 = 1; // for user selected as there is no frame

		// if the result is NULL, its added to alignList as null
		bestData = alignNTdoDP (seq1, seq2, f1, f2, hitData);
		
		if (typeResult==firstResult || typeResult==frameResult) {
			alignList.add(bestData);
		}
		else if (typeResult==bestResult) { // try opposite direction and take best
			curData = alignNTdoDP (seq1, seq2, f1, -f2, hitData);		
			if (curData != null && cmpAlignData(curData, bestData)) 
					bestData = curData;
			alignList.add(bestData);
		}
		else if (typeResult==allResult) {
			alignList.add(bestData);
			curData = alignNTdoDP (seq1, seq2, f1, -f2, hitData);
			if (curData!=null) alignList.add(curData);
		}
	}
	
	static private AlignmentData alignNTdoDP (  
			SequenceData seqData1, SequenceData seqData2, 
			int f1, int f2, BlastHitData hitData) 
	{		
		SequenceData ntSeq1, ntSeq2;
		if ( f1 < 0) ntSeq1 = seqData1.newSeqDataRevComp();
		else ntSeq1 = seqData1.newSeqDataNoQual();
		if ( f2 < 0) ntSeq2 = seqData2.newSeqDataRevComp();
		else ntSeq2 = seqData2.newSeqDataNoQual();
		
		// see comment below for same code in alignAAdoDP
		if (!dpAlgoObj.DPalign ( ntSeq1.getSequence(), ntSeq2.getSequence(), isNT ))
				return null;	
		ntSeq1.buildDPalignedSeq( dpAlgoObj.getHorzResult('.') );
		ntSeq2.buildDPalignedSeq( dpAlgoObj.getVertResult('.') );

		AlignmentData curAlign = new AlignmentData ( 
				label, dpAlgoObj, ctgDataI, ctgDataII, 
				ntSeq1, ntSeq2, 0, 0, hitData, isProteinDB);

		return curAlign;
	} 
	
	/****************************************************************
	 * XXX AA alignment
	 * 1. Core: NT & NT & tblastx: have both frames
	 * 2. Core: NT & NT & blast: have no frames
	 * 3. Disp: NT & NT PW: have both frames from database
	 * 4. Disp: NT & NT DB: have both frames from database
	 * 5. Disp: NT & AA DB: have NT frame from database
	 * 6. Disp: NT & NT: selected by user, don't know frame
	 ***************************************************************/
	static private void alignAA ( SequenceData seq1,  SequenceData seq2, 
			int f1, int f2, BlastHitData hitData) 
	{
		AlignmentData curData = null, bestData=null;
		
		if ((typeResult==frameResult || typeResult==firstResult) && f1!=0) { 	
			bestData = alignAAdoDP (seq1, seq2, f1, f2, hitData);
			alignList.add(bestData);
		}
		else if (f1==0 && f2==0) { 	// AA-AA protein database 
			bestData = alignAAdoDP (seq1, seq2, f1, f2, hitData);
			alignList.add(bestData);
		}
		else if (typeResult==bestResult && f1!=0 && f2==0) { // NT-AA
			int s1, e1;
			if (f1 < 0) 		{s1 = -3; e1 = -1;}
			else if (f1 > 0)	{s1 = 1;  e1 = 3;}
			else 			{s1 = -3; e1 = 3;}
			
			for ( int i = s1; i <= e1; i++ )
			{
				if (i == 0) continue;
				curData = alignAAdoDP (seq1, seq2, i, 0, hitData);	
				if (cmpAlignData(curData, bestData)) 
					bestData = curData;
			}
			alignList.add(bestData);
		}
		else if (typeResult==allResult && f1!=0 && f2==0) { // NT-AA
			for ( int i = 3; i >= -3; --i )
			{
				if (i == 0) continue;
				curData = alignAAdoDP (seq1, seq2, i, 0, hitData);	
				if (curData != null) alignList.add(curData);
			}
		}
		else if (typeResult==bestResult) { // uses megablast pairs orientation
			int s1, e1, s2, e2;
			if (f1 < 0) 		{s1 = -3; e1 = -1;}
			else if (f1 > 0)	{s1 = 1;  e1 = 3;}
			else 			{s1 = -3; e1 = 3;}
			
			if (f2 < 0) 		{s2 = -3; e2 = -1;}
			else if (f2 > 0)	{s2 = 1;  e2 = 3;}
			else 			{s2 = -3; e2 = 3;}
			
			for ( int i = s1; i <= e1; i++ )
				for ( int j = s2; j <= e2; j++ )
				{
					if (i == 0 || j == 0) continue;
					curData = alignAAdoDP (seq1, seq2, i, j, hitData);	
					if (cmpAlignData(curData, bestData)) 
						bestData = curData;
				}
			alignList.add(bestData);
		}
		else if (typeResult==allResult) {
			for ( int i = 3; i >= -3; i-- )
				for ( int j = 3; j >= -3; j-- )
				{
					if (i==0 || j==0) continue;
					curData = alignAAdoDP (seq1, seq2, i, j, hitData);
					if (curData != null) alignList.add(curData);
				}
		}
		else {
			System.err.println("***Internal error: alignAA " + 
					typeResult + " " + f1 + " " + f2);
			System.exit(-1);
		}	
	}
		
    static private AlignmentData alignAAdoDP ( 
    		SequenceData ntSeq1, SequenceData xxSeq2, int f1, int f2, 
    		BlastHitData hitData)
    {
	    	try {
	    		SequenceData aaSeq1, aaSeq2;
	 
			if ( f1 < 0) aaSeq1 = ntSeq1.newSeqDataRevComp().newSeqDataNTtoAA(Math.abs(f1));
			else if (f1 != 0) aaSeq1 = ntSeq1.newSeqDataNTtoAA(f1);
			else aaSeq1 = ntSeq1.newSeqDataNoQual(); // really protein CAS 6/30/13
			
			if ( f2 < 0) aaSeq2 =  xxSeq2.newSeqDataRevComp().newSeqDataNTtoAA(Math.abs(f2));
			else if (f2 !=0) aaSeq2 = xxSeq2.newSeqDataNTtoAA(f2);
			else aaSeq2 = xxSeq2.newSeqDataNoQual(); // protein sequence		
			
			if (!dpAlgoObj.DPalign (aaSeq1.getSequence(), aaSeq2.getSequence(), !isNT ))
						return null;
		
			// getHorzResult builds the alignment and computes the number of matching amino acids
			// processDPalignedSeq changes the sequence data to add gaps (not a copy)	
			aaSeq1.buildDPalignedSeq( dpAlgoObj.getHorzResult('.') );
			aaSeq2.buildDPalignedSeq( dpAlgoObj.getVertResult('.') );	
			
			return new AlignmentData ( 
					label, dpAlgoObj, ctgDataI, ctgDataII, 
					aaSeq1, aaSeq2, f1, f2, hitData, isProteinDB);
	    	}
	    	catch (Exception e) {crash(e, "AA DP alignment");}
	    	return null;
    }
    
    // XXX heuristics
    static private boolean cmpAlignData(AlignmentData cur, 
    						AlignmentData best) 
    {
    		if (best==null) return true;
    		if (cur==null) return false;
 
		return PairwiseAligner.cmpScore(cur.nHSRlen, cur.pHSRsim, cur.nHSRstops, 
				best.nHSRlen, best.pHSRsim, best.nHSRstops);
    } 

    static private void crash(Exception err, String msg) {
   		System.err.println(msg);
   		ErrorReport.reportError(err,msg);
		System.exit(-1);
    }
}
